﻿using System;
using Microsoft.AspNet.SignalR.Client;
using System.Net;

namespace NLog.Targets.SignalR
{
    public class HubProxy : IDisposable
    {
        private readonly SignalRTarget target;
        private IHubProxy proxy;


        private HubConnection connection;
        public HubConnection Connection
        {
            get { return connection; }
        }

        public HubProxy(SignalRTarget target)
        {
            this.target = target;
        }

        public void Log(LogEvent logEvent)
        {
            EnsureProxyExists();

            if (this.proxy != null)
            {
                this.proxy.Invoke(this.target.MethodName, logEvent);
            }
        }

        public void EnsureProxyExists()
        {
            if (this.proxy == null || this.Connection == null)
            {
                this.BeginNewConnection();
            }

            else if (this.Connection.State == ConnectionState.Disconnected)
            {
                this.StartExistingConnection();
            }
        }

        private void BeginNewConnection()
        {
            this.connection = new HubConnection(target.Uri.AbsoluteUri);
            if (this.target.UseDefaultCredentials)
            {
                this.Connection.Credentials = CredentialCache.DefaultNetworkCredentials;
            }
            this.proxy = this.Connection.CreateHubProxy(this.target.HubName);
            this.Connection.Start().ContinueWith(task =>
            {
                if (!task.IsFaulted)
                {
                    this.proxy.Invoke("Notify", this.Connection.ConnectionId);
                }
                else
                {
                    this.proxy = null;
                }
            });
        }

        private void StartExistingConnection()
        {
            this.Connection.Start().ContinueWith(task =>
            {
                if (task.IsFaulted)
                {
                    this.proxy = null;
                }
            });
        }

        #region IDisposable

        private bool disposed = false;

        /// <summary>
        /// Releases all resources used by an instance of the <see cref="HubProxy" /> class.
        /// </summary>
        /// <remarks>
        /// This method calls the virtual <see cref="Dispose(bool)" /> method, passing in true, 
        /// and then suppresses 
        /// finalization of the instance.
        /// </remarks>
        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Releases the unmanaged resources used by an instance of the 
        /// <see cref="HubProxy" /> class and optionally releases the managed resources.
        /// </summary>
        /// <param name="disposing">true to release both managed and unmanaged resources;        
        /// false to release only unmanaged resources.</param>
        protected virtual void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                if (disposing)
                {
                    // free managed resources

                    if (this.connection != null)
                    {
                        this.connection.Dispose();
                        this.connection = null;
                    }
                }

                // free native resources if there are any.

                this.disposed = true;
            }
        }

        /// <summary>
        /// Releases unmanaged resources before an instance of the <see cref="HubProxy" /> 
        /// class is reclaimed by garbage collection.
        /// </summary>
        /// <remarks>
        /// NOTE: Leave out the finalizer altogether if this class doesn't 
        /// own unmanaged resources itself, but leave the other methods
        /// exactly as they are.
        /// This method releases unmanaged resources by calling the virtual 
        /// <see cref="Dispose(bool)" /> method, passing in false.
        /// </remarks>
        ~HubProxy()
        {
            this.Dispose(false);
        }

        #endregion
    }
}
